#!/bin/bash
# This daemon is for spying the changes in /apps, and automatically
# activating or deactivating apps when moving apps in or out of /apps.
# Author: xiooli <xioooli@yahoo.com.cn> <http://joolix.com>
# Licence: GPLv2
# 2009.08.15

. /etc/rc.d/functions

Apps="/apps"
Mnt="/mnt/livesys/mnt"
Lockdir="/var/lock/appd"
[ -d "$Lockdir" ] || mkdir -p "$Lockdir"
Pidfile="/var/run/appd.pid"
Statefile="/var/log/appd/appd.stat"
Logdir="/var/log/appd"
[ -d "$Logdir" ] || mkdir -p "$Logdir"
Maxlogline=1000
[ "$(cat "$Logdir/appd.log" | wc -l)" -ge "$Maxlogline" ] \
&& :> "$Logdir/appd.log"

FIND_DIFF () {
	
	local dir0 dir1

	dir0="$1" # app files inside
	dir1="$2" # mounted dirs inside
	[ -d "$dir0" -a -d "$dir1" ] && {
		( find "$dir0" -mindepth 1 -maxdepth 1 -type f | sed "s|\.app$||"; \
		find "$dir1" -mindepth 1 -maxdepth 1 -type d ) \
		| sed "s|\(^.*\/\)\(.*$\)|\2--MARK--\1|" | sort \
		| sed "s|\(^.*\)--MARK--\(.*$\)|\2--MARK-- \1|" \
		| uniq -f 1 -u | sed "s|--MARK-- ||g;s|${dir0}.*$|&.app|"
	}
}

TYPE () {
	
	if [ -f "$1" -a "${1%.app}" != "$1" ]; then
		[ "x$(echo $1 | grep "$Apps")" != "x" ] && echo app
	elif [ -d "$1" ]; then
		[ "x$(echo $1 | grep "$Mnt")" != "x" ] && echo dir
	else
		echo unknown
	fi
}

DEACTIVATE_BLOCK () {
	
	local appname sleeptm
	appname="$1"
	sleeptm="$2"
	[ "${#@}" -ge 2 ] && {
	if [ ! -e "$Lockdir/$appname.deactivate-block" ]; then
		:> "$Lockdir/$appname.deactivate-block"
		if [ "$2" != "0" ]; then
			(sleep "$sleeptm" && rm "$Lockdir/$appname.deactivate-block") &
		fi
	fi
	}
}

ACTIVATEAPP_STAT () {
	
	local stat
	stat="$1"
	case "$stat" in
		0) echo "App activated successfully!" ;;
		1) echo "Permission denied, only root can exec this!" ;;
		2) echo "App either dosen't exist or is a dir." ;;
		3) echo "App file must end with .app" ;;
		4) echo "Not in the live mode." ;;
		5) echo "App with the same name already exits in /apps" ;;
		6) echo "Error copying app, not enough free space." ;;
		7) echo "Error inserting app to live filesystem." ;;
		*) echo "Unknown state" ;;
	esac
}

DEACTIVATEAPP_STAT () {
	
	local stat appnm
	stat="$1"
	appnm="$(basename "$2")"
	case "$stat" in
		0) echo "App deactivated successfully!" ;;
		1) echo "Permission denied, only root can exec this!" ;;
		2) echo "App not given." ;;
		3) 
		echo "Can't deactivate the given app, it might be essential app." 
		DEACTIVATE_BLOCK "$appnm" 0
		;;
		4) 
		echo "App is busy (used)." 
		DEACTIVATE_BLOCK "$appnm" 300
		;;
		5)
		echo "Umount app mount point failed." 
		DEACTIVATE_BLOCK "$appnm" 300
		;;
		6)
		echo "Mount point not empty." 
		DEACTIVATE_BLOCK "$appnm" 0
		;;
		7)
		echo "This app was activated outside the union, can not deactivate inside."
		DEACTIVATE_BLOCK "$appnm" 0
		;;
		*) 
		echo "Unknown state"
		DEACTIVATE_BLOCK "$appnm" 300
		;;
	esac
}

LOG () {
	echo $@ >> "$Logdir/appd.log"
}

RUN () {
	
	local line stat
	
	while :; do
		FIND_DIFF "$Apps" "$Mnt" | while read line; do
		if [ "$(TYPE "$line")" = "app" ]; then
			LOG "`date`: Activating `basename "$line"`"
			/sbin/activateapp "$line"
			stat0="$?"
			stat1="A-$stat0"
			LOG "STATE-$stat0 `date`: `ACTIVATEAPP_STAT "$stat0"`"
		elif [ "$(TYPE "$line")" = "dir" ]; then
			[ -e "$Lockdir/$(basename "$line").deactivate-block" ] && continue
			LOG "`date`: Dectivating `basename "$line"`.app"
			/sbin/deactivateapp "$line"
			stat0="$?"
			stat1="D-$stat0"
			LOG "STATE-$stat0 `date`: `DEACTIVATEAPP_STAT "$stat0" "$(basename "$line")"`"
		fi
		[ "x$stat1" != "x" ] && echo "$stat1" > "$Statefile"
		stat1=""
		done
		sleep 5
	done
}

case "$1" in
	start)
	if [ -e "$Pidfile" ]; then
	   stat_fail
	else
		stat_busy "Starting appd..."
		LOG "`date`: Starting appd..."
		RUN 2>>"$Logdir/appd.err" & 
		echo "$!" > "$Pidfile"
		if [ -e "$Pidfile" ]; then
			stat_done
			add_daemon appd
			LOG "`date`: appd started."
		else
			stat_fail
			LOG "`date`: starting appd failed"
		fi
	fi		
	;;
	stop)
	if [ -e "$Pidfile" ]; then
		stat_busy "Terminating appd daemon..."
		LOG "`date`: Terminating appd daemon..."
		kill -9 "$(cat "$Pidfile")" 2>> $Logdir/appd.err
		[ $? -eq 0 ] && {
		stat_done
		rm_daemon appd
		LOG "`date`: appd terminated." 
		} && rm "$Pidfile"
	else
		stat_fail
		LOG "`date`: stop appd, appd not runing"
	fi
	;;
	restart)
	$0 stop
	$0 start
	;;
esac
